﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using DCS.AdministrationClient.DCS_Service;

// Web reference

namespace DCS.AdministrationClient.Forms
{
    public partial class TaskPreparationForm : Form
    {
        private Postprocessing mark;

        /// <summary> 
        /// Индикатор необходимости сохранения данных
        /// о текущем задании в файл на диске. 
        /// </summary>
        private bool toSaveTask;

        private bool argumentsSet;

        /// <summary> Состояние текущего задания. </summary>
        private TaskState currentState = TaskState.New;

        private TaskPack currentTaskPack = new TaskPack();

        /// <summary> Индикатор внесений изменений в текущее задание. </summary>
        private bool isTaskChanged;

        /// <summary> Путь к файлу задания. </summary>
        private string taskFilePath = "";

        public TaskPreparationForm()
        {
            InitializeComponent();

            CheckForIllegalCrossThreadCalls = false;

            priorityComboBox.SelectedIndex = 1;
        }

        public TaskPack Task
        {
            get { return currentTaskPack; }
        }

        private void BrowseButtonClick(object sender, EventArgs e)
        {
            // Если в диалоге выбран файл
            if (openCalculatingModuleFileDialog.ShowDialog() == DialogResult.OK)
            {
                MarkTaskModified();

                pathTextBox.Text = openCalculatingModuleFileDialog.FileName;
                taskFilePath = pathTextBox.Text;
            }
        }

        private void ButtonDependencyAddClick(object sender, EventArgs e)
        {
            isTaskChanged = true;

            // Фильтр на расширение файла вычислительного модуля
            openDependencyFileDialog.Filter = "All files (*.*)|*.*";

            // Убераем имя файла по умочанию
            openDependencyFileDialog.FileName = "";

            openDependencyFileDialog.FilterIndex = 2;

            openDependencyFileDialog.RestoreDirectory = true;

            // Если в диалоге выбран файл(ы) ...
            if (openDependencyFileDialog.ShowDialog() == DialogResult.OK)
            {
                string fileName;

                foreach (string name in openDependencyFileDialog.FileNames)
                {
                    fileName = name;

                    string imageKey = fileName.Substring(fileName.LastIndexOf(".") + 1, 3) + ".png";

                    listViewDependencyFiles.Items.Add(fileName, imageKey);
                }
            }
        }

        private void ButtonDependencyDeleteClick(object sender, EventArgs e)
        {
            for (int i = 0; i < listViewDependencyFiles.SelectedItems.Count; i++)
            {
                isTaskChanged = true;

                listViewDependencyFiles.Items.Remove(listViewDependencyFiles.SelectedItems[i]);
            }
        }

        private void ButtonDependenciesDeleteAllClick(object sender, EventArgs e)
        {
            isTaskChanged = true;

            listViewDependencyFiles.Items.Clear();
        }

        private void ButtonOkClick(object sender, EventArgs e)
        {
            if (currentState == TaskState.New)
            {
                return;
            }

            ExitWithConfirm(DialogResult.OK);
        }


        /// <summary>
        /// Выход из программы с подтверждением.
        /// </summary>
        /// <param name="result"> Результат дилога с текущим окном. </param>
        private void ExitWithConfirm(DialogResult result)
        {
            if (result == DialogResult.OK)
            {
                mark = new Postprocessing(SetDialogResultOk);
            }
            if (result == DialogResult.Cancel)
            {
                mark = new Postprocessing(SetDialogResultCancel);
            }

            if (isTaskChanged)
            {
                if (
                    MessageBox.Show("Сохранить сделанные изменения в файл?", "Сохранить?", MessageBoxButtons.YesNo,
                                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    if (taskFilePath != "")
                    {
                        toSaveTask = true;
                    }
                    else
                    {
                        if (saveTaskFileDialog.ShowDialog() == DialogResult.OK)
                        {
                            taskFilePath = saveTaskFileDialog.FileName;
                            Text = taskFilePath;
                            toSaveTask = true;
                        }
                        else
                        {
                            // Пользователь отменил выход
                            return;
                        }
                    }
                }
                else
                {
                    toSaveTask = false;
                }
            }

            // Запуск сохранения + выход
            backgroundWorker.RunWorkerAsync();
        }


        /// <summary>
        /// Устанавливает результат диалога OK.
        /// </summary>
        private void SetDialogResultOk()
        {
            // Завершаем диалог, информируя о том что изменения приняты
            DialogResult = DialogResult.OK;
        }

        /// <summary>
        /// Устанавливает результат диалога Cancel.
        /// </summary>
        private void SetDialogResultCancel()
        {
            // Завершаем диалог, информируя о том что изменения приняты
            DialogResult = DialogResult.Cancel;
        }

        private void ButtonExitClick(object sender, EventArgs e)
        {
            ExitWithConfirm(DialogResult.Cancel);
        }

        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            // Если задача успешно считана ...
            if (SetTask(backgroundWorker))
            {
                if (toSaveTask)
                {
                    SaveTask(taskFilePath);
                }
            }
            if (mark != null)
            {
                mark();
                mark = null;
            }
        }

        private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
        }

        private void PathTextBoxMouseClick(object sender, MouseEventArgs e)
        {
            if (currentState == TaskState.New)
            {
                MarkTaskModified();
                pathTextBox.Text = "";
            }
        }

        private void TaskChanged(object sender, EventArgs e)
        {
            isTaskChanged = true;
        }

        private void TextBoxAdditionalArgumentsMouseClick(object sender, MouseEventArgs e)
        {
            if (!argumentsSet)
            {
                argumentsSet = true;
                textBoxAdditionalArguments.Text = "";
            }
        }

        #region Menu items

        private void SaveNewTaskClick(object sender, EventArgs e)
        {
            mark = new Postprocessing(MarkTaskNew);

            // Если задание редактировалось с момента последнего сохранения
            if (isTaskChanged)
            {
                // Убеждаемся нужно ли сохранять текущую задачу
                DialogResult dialogResult = MessageBox.Show("Сохранить сделанные изменения в файл?", "Сохранить?",
                                                            MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dialogResult == DialogResult.Yes)
                {
                    if (taskFilePath != "")
                    {
                        // Запускаем считывание - сохранение
                        backgroundWorker.RunWorkerAsync();
                    }
                    else
                    {
                        if (saveTaskFileDialog.ShowDialog() == DialogResult.OK)
                        {
                            // Запоминаем выбранный путь
                            taskFilePath = saveTaskFileDialog.FileName;

                            Text = taskFilePath;

                            // Запускаем считывание - сохранение
                            backgroundWorker.RunWorkerAsync();
                        }
                        else
                        {
                            // ... пользователь передумал
                            return;
                        }
                    }
                }

                else
                {
                    // Очищаем форму
                    mark();
                }
            }
            else
            {
                // Очищаем форму
                mark();
            }
        }


        private void OpenTaskFileToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (isTaskChanged)
            {
                // Убеждаемся нужно ли сохранять задачу в файл
                if (
                    MessageBox.Show("Сохранить сделанные изменения в файл?", "Сохранить?", MessageBoxButtons.YesNo,
                                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    if (taskFilePath != "")
                    {
                        // Запускаем считывание - сохранение
                        backgroundWorker.RunWorkerAsync();
                    }
                    else
                    {
                        if (saveTaskFileDialog.ShowDialog() == DialogResult.OK)
                        {
                            // Запоминаем выбранный путь
                            taskFilePath = saveTaskFileDialog.FileName;
                            Text = taskFilePath;

                            // Запускаем считывание - сохранение
                            backgroundWorker.RunWorkerAsync();
                        }
                        else
                        {
                            // ... пользователь передумал
                            return;
                        }
                    }
                }
            }

            // Запускаем диалог выбора файла для открытия
            openTaskFileDialog.Filter = ".DCS task files (*.tsk)|*.tsk";

            if (openTaskFileDialog.ShowDialog() == DialogResult.OK)
            {
                // Сохраняем путь к файлу
                taskFilePath = openTaskFileDialog.FileName;
                Text = taskFilePath;

                // Открываем задание
                OpenTask();
            }
        }

        private void SaveClick(object sender, EventArgs e)
        {
            mark = null;

            toSaveTask = true;

            if (taskFilePath != "")
            {
                isTaskChanged = false;

                // Запускаем считывание - сохранение                
                backgroundWorker.RunWorkerAsync();
            }
            else
            {
                if (saveTaskFileDialog.ShowDialog() == DialogResult.OK)
                {
                    isTaskChanged = false;

                    // Запоминаем выбранный путь
                    taskFilePath = saveTaskFileDialog.FileName;
                    Text = taskFilePath;

                    // Запускаем считывание - сохранение
                    backgroundWorker.RunWorkerAsync();
                }
                else
                {
                    // ... пользователь передумал
                    return;
                }
            }
        }

        private void SaveAsClick(object sender, EventArgs e)
        {
            mark = null;

            toSaveTask = true;

            if (saveTaskFileDialog.ShowDialog() == DialogResult.OK)
            {
                isTaskChanged = false;

                // Запоминаем выбранный путь
                taskFilePath = saveTaskFileDialog.FileName;
                Text = taskFilePath;

                // Запускаем считывание - сохранение
                backgroundWorker.RunWorkerAsync();
            }
            else
            {
                // ... пользователь передумал
                return;
            }
        }

        private void DeleteTaskClick(object sender, EventArgs e)
        {
            // Спрашиваем подтврждение пользователя на удаление задания с диска
            DialogResult result = MessageBox.Show("Вы действительно хотите удалить данное задание с диска?", "Удаление",
                                                  MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                                                  MessageBoxDefaultButton.Button2);
            if (result == DialogResult.Yes)
            {
                // Удаляем файл задачи
                File.Delete(taskFilePath);

                // Приводим форму к начальному состоянию
                MarkTaskNew();
            }
        }

        private void AcceptTaskToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (currentState == TaskState.New)
            {
                return;
            }

            ExitWithConfirm(DialogResult.OK);
        }

        private void ExitToolStripMenuItemClick(object sender, EventArgs e)
        {
            ExitWithConfirm(DialogResult.Cancel);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Помечает задание как новое.
        /// </summary>
        private void MarkTaskNew()
        {
            // Путь к вычислительному модулю
            pathTextBox.Text = "Введите путь к исполняемому файлу или сборке .dll ...";

            // Очистка списка зависимостей
            listViewDependencyFiles.Items.Clear();

            // Имя задачи
            textBoxTaskName.Text = "Полезная задача";

            // Число порций
            numericUpDownPortionsNumber.Value = 20;

            // Дедлайн
            maskedTextBoxDeadline.Text = "311220500000";

            // Приоритет
            priorityComboBox.SelectedIndex = 1;

            pathTextBox.ForeColor = Color.Gray;

            browseButton.Enabled = true;

            buttonDependencyAdd.Enabled = true;
            buttonDependencyDelete.Enabled = true;
            buttonDependenciesDeleteAll.Enabled = true;

            currentState = TaskState.New;
            taskFilePath = "";

            isTaskChanged = false;
        }

        /// <summary>
        /// Помечает задание как изменённое.
        /// </summary>
        private void MarkTaskModified()
        {
            pathTextBox.ForeColor = Color.Black;

            browseButton.Enabled = true;

            buttonDependencyAdd.Enabled = true;
            buttonDependencyDelete.Enabled = true;
            buttonDependenciesDeleteAll.Enabled = true;

            currentState = TaskState.Modified;
        }


        /// <summary>
        /// Считывает данные задачи из файлов и с формы в объект TaskPackStruct.
        /// </summary>
        /// <param name="worker"> Объект BackgoundWorker для обеспечения работы в параллельном потоке. </param>
        /// <returns> Результат формирования объекта TaskPackStruct. </returns>
        private bool SetTask(BackgroundWorker worker)
        {
            // Создаём для задачи новый уникальный идентификатор
            currentTaskPack.Guid = Guid.NewGuid();

            // Устанавливаем имя задачи
            currentTaskPack.TaskName = textBoxTaskName.Text;

            // Устанавливаем deadline задачи
            currentTaskPack.Deadline = Convert.ToDateTime(maskedTextBoxDeadline.Text);

            // Устанавливаем число порций            
            currentTaskPack.PortionsNumber = Convert.ToUInt32(numericUpDownPortionsNumber.Value);

            // Устанавливаем приоритет задания
            currentTaskPack.Priority = Convert.ToInt16(priorityComboBox.SelectedIndex);

            currentTaskPack.Summary = richTextBoxSummary.Text;

            currentTaskPack.AdditionalArguments = textBoxAdditionalArguments.Text;

            if (currentState != TaskState.LoadedFromFile)
            {
                // Устанавливаем вычислительный модуль задачи
                try
                {
                    if (taskFilePath != "")
                    {
                        // Создаём объект вычислительного модуля
                        currentTaskPack.ComputationModule = new ComputationModule
                                                                {
                                                                    ModuleName =
                                                                        pathTextBox.Text.Substring(
                                                                            pathTextBox.Text.LastIndexOf('\\') + 1),
                                                                    ComputationModuleData =
                                                                        File.ReadAllBytes(pathTextBox.Text)
                                                                };

                        // Присваиваем имя вычислительного модуля - имя файла

                        // Считываем бинарные данные в вычислительный модуль
                    }

                    worker.ReportProgress(5);
                }
                catch (Exception exc)
                {
                    if (currentState == TaskState.New)
                    {
                        MessageBox.Show(
                            "Ошибка при загрузке вычислительного модуля.\nВведите путь к исполняемому файлу или сборке .dll",
                            "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
                    }
                    else
                    {
                        MessageBox.Show("Ошибка при загрузке вычислительного модуля.\n" + exc.Message, "Ошибка",
                                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
                                        MessageBoxDefaultButton.Button1);
                    }
                    return false;
                }

                // Добавляем файлы задачи
                int dependenciesCount = listViewDependencyFiles.Items.Count;

                double progress = 95.0/dependenciesCount;

                try
                {
                    // Создаём массив зависимостей
                    currentTaskPack.Dependencies = new Dependency[dependenciesCount];

                    for (int i = 0; i < dependenciesCount; i++)
                    {
                        // Путь к файлу
                        string filePath = listViewDependencyFiles.Items[i].Text;

                        currentTaskPack.Dependencies[i] = new Dependency
                                                              {
                                                                  // Добавляем имя зависимости
                                                                  DependencyName =
                                                                      filePath.Substring(filePath.LastIndexOf('\\') + 1),
                                                                  // Добавляем бинарные данные зависимости
                                                                  DependencyData = File.ReadAllBytes(filePath)
                                                              };

                        // Сообщаем о текущем прогрессе
                        worker.ReportProgress((int) progress*i);
                    }

                    worker.ReportProgress(100);

                    return true;
                }
                catch (Exception exc)
                {
                    MessageBox.Show("Ошибка при загрузке дополнительного файла:\n" + exc.Message, "Ошибка",
                                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);

                    for (int i = 100; i >= 0; i--)
                    {
                        worker.ReportProgress(i);
                    }
                    return false;
                }
            }
            return true;
        }


        /// <summary>
        /// Сохраняет данные о задачи в файл.
        /// </summary>
        /// <param name="path"> Путь к сохраняемому файлу. </param>
        /// <returns> Результат сохранения в файл. </returns>        
        private void SaveTask(string path)
        {
            try
            {
                // Создаём новый поток filestream для записи пакета задачи в файл
                var fileStream = new FileStream(path, FileMode.Create);

                // Создаём объект BinaryFormatter для сериализации объекта
                var binaryFormatter = new BinaryFormatter();

                // Сериализуем объект
                binaryFormatter.Serialize(fileStream, currentTaskPack);

                statusLabel.Text = "Задача успешно сохранена. Размер пакета: " +
                                   (fileStream.Length/(double) 1024).ToString("N2") + " kb";

                // Закрываем поток
                fileStream.Close();

                // Фиксируем изменения
                isTaskChanged = false;

                return;
            }
            catch
            {
                MessageBox.Show("Ошибка сохранения файла задачи. Проверьте имя файла.", "Ошибка", MessageBoxButtons.OK,
                                MessageBoxIcon.Error);

                statusLabel.Text = "Произошла ошибка при сохранения файла задачи";

                return;
            }
        }


        /// <summary>
        /// Отображает данные о задаче на форме.
        /// </summary>
        /// <returns> Результат отображения на форме. </returns>
        private void BrowseTask()
        {
            // Очищаем списк зависимостей
            listViewDependencyFiles.Clear();

            try
            {
                // Пытаемся считать имя задачи
                pathTextBox.Text = currentTaskPack.ComputationModule.ModuleName;
            }
            catch (Exception)
            {
            }

            try
            {
                textBoxAdditionalArguments.Text = currentTaskPack.AdditionalArguments;
            }
            catch (Exception)
            {
            }

            // Перебираем все зависимости
            foreach (var dependency in currentTaskPack.Dependencies)
            {
                try
                {
                    // Пытаемся добавить имя зависимости на форму
                    listViewDependencyFiles.Items.Add(dependency.DependencyName);
                }
                catch (Exception)
                {
                }
            }

            try
            {
                // Пытаемся считать имя задачи
                textBoxTaskName.Text = currentTaskPack.TaskName;
            }
            catch (Exception)
            {
            }

            try
            {
                // Пытаемся считать число порций
                numericUpDownPortionsNumber.Value = currentTaskPack.PortionsNumber;
            }
            catch
            {
            }

            try
            {
                // Пытаемся считать дедлайн
                maskedTextBoxDeadline.Text = currentTaskPack.Deadline.ToString();
            }
            catch
            {
            }

            try
            {
                // Пытаемся считать приоритет
                priorityComboBox.SelectedIndex = currentTaskPack.Priority;
            }
            catch (Exception)
            {
            }

            try
            {
                // Пытаемся считать summary
                richTextBoxSummary.Text = currentTaskPack.Summary;
            }
            catch (Exception)
            {
            }

            return;
        }

        /// <summary>
        /// Открыть задание из файла.
        /// </summary>        
        /// <returns> Результат чтения данных из файла. </returns>
        private void OpenTask()
        {
            try
            {
                // Создаём поток для чтения файла задачи
                using (var fileStream = new FileStream(openTaskFileDialog.FileName, FileMode.Open))
                {
                    // Создайм Formatter для десериализации объекта из потока
                    var binaryFormatter = new BinaryFormatter();

                    // Десериализуем объект
                    currentTaskPack = (TaskPack) binaryFormatter.Deserialize(fileStream);

                    // Отображаем считанный объект на форме
                    BrowseTask();

                    // Отображаем информацию
                    statusLabel.Text = string.Format("Прочитано задание {0}. Размер пакета: {1} kb",
                                                     currentTaskPack.TaskName,
                                                     (fileStream.Length/(double) 1024).ToString("N2"));

                    // Помечаем задачу как загруженную из файла
                    MarkTaskLoaded();

                    isTaskChanged = false;

                    statusLabel.Text = string.Format("Задача прочитана из файла. Размер пакета: {0} kb",
                                                     (fileStream.Length/(double) 1024).ToString("N2"));
                }
            }
            catch
            {
                MessageBox.Show("Ошибка при загрузке файла задачи. Файл повреждён или имеет неверный формат.",
                                "Ошибка загрузки файла.", MessageBoxButtons.OK, MessageBoxIcon.Error);
                statusLabel.Text = "Ошибка при загрузке файла задачи.";
            }

            return;
        }


        /// <summary>
        /// Помечает задание как загруженное из файла.
        /// </summary>
        private void MarkTaskLoaded()
        {
            pathTextBox.ForeColor = Color.Gray;

            browseButton.Enabled = false;

            buttonDependencyAdd.Enabled = false;
            buttonDependencyDelete.Enabled = false;
            buttonDependenciesDeleteAll.Enabled = false;

            currentState = TaskState.LoadedFromFile;
        }

        #endregion

        private delegate void Postprocessing();

    }
}